<?php

/**
 * Framework_Util_LdapConnection
 *
 * @author Application Support Group
 * @copyright University of Georgia
 * @package BAMF
 *
 */
class Framework_Util_LdapConnection {

	protected $_url;
	protected $_base_dn;
	protected $_connection;
	protected $_anonymous;
	protected $_username;
	protected $_usernameField;



	/**
	 * __construct()
	 *
	 * @param string $aURL The URL of the LDAP server
	 * @param string $aDN Some base LDAP division references, such as organization and organizational unit
	 *
	 * @package BAMF
	 *
	 */
	public function __construct( $aURL=null, $aDN=null ) {

		if ( empty( $aURL )) {
			$this->_url     = Framework_ErrorHandler::raiseError( "Invalid connection information (URL).", __METHOD__."::".__LINE__ ) ;
		} else if ( empty( $aDN )) {
			$this->_base_dn = Framework_ErrorHandler::raiseError( "Invalid connection information (dn).",  __METHOD__."::".__LINE__ ) ;
		} else {
			$this->_url     = $aURL ;
			$this->_base_dn = $aDN ;
		}
	}



	/**
	 * connect()
	 *
	 * @param string $aUsername (optional, default: null)
	 * @param string $aPassword (optional, default: null)
	 * @param string $usernameField (optional, default: "cn")
	 *
	 * @return mixed TRUE on success, FrameworkError otherwise.
	 *
	 * @package BAMF
	 *
	 */
	public function connect( $aUsername = null, $aPassword = null, $usernameField = "cn" ) {
		$_output   = FALSE;
		$anonymous = FALSE;
		if ( is_null( $aUsername ) || is_null( $aPassword )) {
			$aUsername     = null;
			$aPassword     = null;
			$usernameField = null;
			$anonymous     = TRUE;
		} else {
			$aUsername = trim( $aUsername ) ;
			$aPassword = trim( $aPassword ) ;
		}
		
		if ( $anonymous || !empty( $aUsername )) {
			if ( $anonymous || !empty( $aPassword )) {
				if ( $this->_connection = ldap_connect( $this->_url )) {

					$this->_username      = $aUsername;
					$this->_usernameField = $usernameField;
					$this->_anonymous     = $anonymous;

					if ( $anonymous ) {
						if ( @ldap_bind( $this->_connection )) {
							$_output = TRUE;
						} else {
							$_output = Framework_ErrorHandler::raiseError( "LDAP: ".ldap_error( $this->_connection ), __METHOD__."::".__LINE__) ;
						}
					} else {
						$full_dn = $this->_usernameField."=".$this->_username.",".$this->_base_dn;
						if ( @ldap_bind( $this->_connection, $full_dn, $aPassword )) {
							$_output = TRUE;
						} else {
							$_output = Framework_ErrorHandler::raiseError( "LDAP: ".ldap_error( $this->_connection ), __METHOD__."::".__LINE__) ;
						}
					}

				} else {
					$_output = Framework_ErrorHandler::raiseError( "Could not connect to LDAP server.", __METHOD__."::".__LINE__ ) ;
				}
			} else {
				$_output = Framework_ErrorHandler::raiseError( "Password is required input.", __METHOD__."::".__LINE__ ) ;
			}
		} else {
			$_output = Framework_ErrorHandler::raiseError( "Username is required input.", __METHOD__."::".__LINE__ ) ;
		}

		return $_output;
	}



	/**
	 * search()
	 *
	 * @param string  $filter
	 * @param mixed   $returnAttributes an array of strings, or null to return values for all attributes
	 * @param boolean $returnAttribArrays (optional, default: FALSE)
	 * @param string  $search_dn (optional) if different from base_dn value passed into contructor
	 * @param int     $maxResultCount (optional, default: 0) maximum number of results to return, or 0 to return all results
	 *
	 * @return mixed Array of entries, or a FrameworkError on error.
	 *
	 * @package BAMF
	 *
	 */
	public function search( $filter = "", $returnAttributes = null, $returnAttribArrays = FALSE, $search_dn = null, $maxResultCount = 0 ) {
		$_output = array();
		if ( is_null( $search_dn )) {
			$search_dn = $this->_base_dn;
		}
		
		$full_dn = (
				$this->_anonymous ?
				"" : $this->_usernameField."=".$this->_username.","
			).$search_dn;
			
		$results = @ldap_search( $this->_connection, $full_dn, $filter );
		if ($results) {

			$entry = @ldap_first_entry( $this->_connection, $results );
			if ( $entry !== FALSE ) {
				while ( $entry !== FALSE && ( $i < $maxResultCount || $maxResultCount <= 0 )) {
					$_output[] = $this->getRequestedAttributes( $entry, $returnAttributes, $returnAttribArrays );
					$entry = @ldap_next_entry( $this->_connection, $entry );
				}
			} else {
				$_output = Framework_ErrorHandler::raiseError( "LDAP: ".ldap_error( $this->_connection ), __METHOD__."::".__LINE__ );
			}
		} else {
			$_output = Framework_ErrorHandler::raiseError( "LDAP: ".ldap_error( $this->_connection ), __METHOD__."::".__LINE__ );
		}
		return $_output;
	}



	/**
	 * getRequestedAttributes()
	 *
	 * @param resource $entry
	 * @param mixed    $returnAttributes an array of strings, or null to return values for all attributes
	 * @param boolean  $returnAttribArrays
	 *
	 * @return mixed Array of attributes.
	 *
	 * @package BAMF
	 *
	 */
	private function getRequestedAttributes( $entry, $returnAttributes = null, $returnAttribArrays ) {
		$_output = array();

		$attributes = @ldap_get_attributes( $this->_connection, $entry );
		if ( $attributes ) {
			if ( is_null( $returnAttributes )) {
				foreach ( $attributes as $key => $val ) {
					if ( is_array( $val ) && !$returnAttribArrays ) {
						$_output[$key] = $val[0];
					} else {
						$_output[$key] = $val;
					}
				}
			} else {
				for ( $j = 0; $j < count($returnAttributes); $j++ ) {
					$thisKey = $returnAttributes[$j];
					if( !empty( $attributes[$thisKey] )) {
						$thisVal = $attributes[$thisKey];
						if ( is_array( $thisVal ) && !$returnAttribArrays ) {
							$_output[$thisKey] = $thisVal[0];
						} else {
							$_output[$thisKey] = $thisVal;
						}
					} else {
						//no value(s) associated with this key
					}
				}
			}
		} else {
			$_output = Framework_ErrorHandler::raiseError( "LDAP: ".ldap_error( $this->_connection ), __METHOD__."::".__LINE__ );
		}
		return $_output;
	}



	/**
	 * __destruct()
	 *
	 * @package BAMF
	 *
	 */	
	public function __destruct() {
		if ( !empty( $this->_connection )) {
			@ldap_unbind( $this->_connection );
		}
	}
}

?>
